#include <windows.h>
#include "dxManager.h"
#include "DInput.h"
#include "DirectSound.h"

// Adding in the labraerys from the console othello
#include <stdlib.h>
#include "Game.h"
#include "Defines.h"
#include "resource.h"
#include "string"

using namespace std;
// end

HINSTANCE hInst;				// holds the instance for this app
HWND wndHandle;					// global window handle

bool bFullScreen = false;

// this holds the DirectX Manager
dxManager *dxMgr;

// Initialize date
LPDIRECTINPUT8        g_lpDI;	// the direct input object
LPDIRECTINPUTDEVICE8  g_lpDIDevice; // the direct input device


// this is the surface that will hold the sprites
IDirect3DSurface9* GreenSurface;
IDirect3DSurface9* BlackSurface;
IDirect3DSurface9* WhiteSurface;
IDirect3DSurface9* CursorSurface;
IDirect3DSurface9* BoarderSurface;
IDirect3DSurface9* TitleSurface;
IDirect3DSurface9* SetupGameSurface;
IDirect3DSurface9* CheckMarkSurface;
IDirect3DSurface9* WaitingForOtherPlayersSurface;



// RECT Title 
RECT destRectTitle;
RECT SourceTitle;
// RECT Boarder 
RECT destRectBoarder;
RECT SourceBoarder;
// RECT Coursour
RECT destRect;
RECT Source;
// RECT Menu
RECT destRectMenu;
RECT SourceMenu;
// RECT Checkmark
RECT destRectCheck;
RECT SourceCheck;
// RECT Checkmark
RECT destRectWait;
RECT SourceWait;

char IPinput[16];
// this is the sprite structure
struct {
	RECT srcRect;				// holds the location of this sprite
								// in the source bitmap
	int posX;					// the sprites X position
	int posY;					// the sprites Y position
} spriteStruct[CALofSPRITES][ROWofSPRITES];

////////////////////////////////////////////// forward declarations
bool    initWindow(HINSTANCE hInstance);
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);

// sprite functions
bool initSprites(void);

#define KEYDOWN(name, key) (name[key] & 0x80) 

void setupNetwork(Game &);
void drawBoard(Game &);

void drawSetupHost();
void drawSetupJoin();

void onLostDevice();
void onResetDevice();

INT_PTR CALLBACK DialogMessages(HWND hwnd,UINT uMsg,WPARAM wparam,LPARAM lparam)
{
    //when the dialog box is first
    //initialized have it send the string " Type in your
    //first name" to our Static Text control
    if( uMsg == WM_INITDIALOG )
    {
        char string [260]= "Type in hosts IP Address";

        SetDlgItemText( hwnd, IDC_TEXT1, string );
	}

    if ( uMsg == WM_COMMAND )
    {
        //when the OK button is clicked after
        //the user enters their name, open a messsage box showing
        //the name they entered :)
        if ( HIWORD(wparam) == BN_CLICKED )
        {
            if (LOWORD(wparam) == IDOK)
            {

                //get the name entered from
                //our Edit Control
                GetDlgItemText( hwnd, IDC_EDIT1, IPinput, 16 );


                EndDialog( hwnd, 0 );
                return TRUE;
            }
      
            else if (LOWORD(wparam) == IDCANCEL)
            {
                EndDialog( hwnd, 0 );
                return TRUE;
            }
        }
    }

return FALSE;
}


int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow)
{
	CDirectSound DSound;
	char buffer[256]; 
	char input = 0;
	Game g;
	char prevInput[256];
	ZeroMemory( &prevInput, sizeof(prevInput) );

	bFullScreen = false;	

	// call our function to init and create our window
	if (!initWindow(hInstance))
	{
		MessageBox(NULL, "Unable to create window", "ERROR", MB_OK);
		return false;
	}

	// create the directx manager
	dxMgr = new dxManager();

	// init the directx manager
	if (!dxMgr->init(wndHandle,bFullScreen))
	{
		MessageBox(NULL, "Unable to Init the DirectX Manager", "ERROR", MB_OK);
		return false;
	}

	// initialise the sprites
	if (!initSprites())
	{
		MessageBox(NULL, "Unable to Init the Sprites", "ERROR", MB_OK);
		return false;
	}
	HRESULT hr;
	// Create the DirectInput object. 
    hr = DirectInput8Create(hInstance, DIRECTINPUT_VERSION, 
                            IID_IDirectInput8, (void**)&g_lpDI, NULL); 

    // Retrieve a pointer to an IDirectInputDevice8 interface 
    hr = g_lpDI->CreateDevice(GUID_SysKeyboard, &g_lpDIDevice, NULL); 

	hr = g_lpDIDevice->SetDataFormat(&c_dfDIKeyboard); 

	if FAILED(hr) { 
		return FALSE; 
	} 

	// Set the cooperative level 
    hr = g_lpDIDevice->SetCooperativeLevel(wndHandle, 
                             DISCL_FOREGROUND | DISCL_NONEXCLUSIVE); 
    if FAILED(hr) 
    { 
        return FALSE; 
    } 

    // Get access to the input device. 
    hr = g_lpDIDevice->Acquire(); 
    if FAILED(hr) 
    { 
        return FALSE; 
    } 
	// Main message loop:
	// Enter the message loop
    MSG msg; 
    ZeroMemory( &msg, sizeof(msg) );

	DSound.SetWindowHandle (wndHandle);
	DSound.InitDirectSound();
	// Sets up the game board (with the first four tiles)
	g.setupBoard();



	// Game loop..
   do{	

		// check for messages
		if( PeekMessage( &msg, NULL, 0U, 0U, PM_REMOVE ) )
        {
			TranslateMessage( &msg );
            DispatchMessage( &msg );
        }

		if(g.getGameStatus() == SETUPGAME)
		{

			// Prints menu and gets input for Host/Join/Single
			setupNetwork(g);

			hr = g_lpDIDevice->GetDeviceState(sizeof(buffer),(LPVOID)&buffer); 

			if (KEYDOWN(buffer, DIK_ESCAPE))
			{
				if(KEYDOWN(prevInput,DIK_ESCAPE))
				{
					Sleep(100);
					ZeroMemory( &prevInput, sizeof(prevInput) );
				}else{
					// shutdown the directX Manager
					dxMgr->shutdown();
					return (int) msg.wParam;
					exit(0);
				}
			}else if (KEYDOWN(buffer, DIK_UP))
			{
				if(KEYDOWN(prevInput,DIK_UP))
				{
					Sleep(100);
					ZeroMemory( &prevInput, sizeof(prevInput) );
				}else{
					if(g.getMenuY() == 2)
					{
						g.decreaseMenuY();
					}else if(g.getMenuY() == 0){
						g.setMenuY(2);
					}else{
						g.decreaseMenuY();
					}
				}

			}else if (KEYDOWN(buffer, DIK_DOWN))
			{
				if(KEYDOWN(prevInput,DIK_DOWN))
				{
					Sleep(100);
					ZeroMemory( &prevInput, sizeof(prevInput) );
				}else{
					if(g.getMenuY() == 0)
					{
						g.increaseMenuY();
					}else if(g.getMenuY() == 2){
						g.setMenuY(0);
					}else{
						g.increaseMenuY();
					}
				}
			}else if (KEYDOWN(buffer, DIK_SPACE))
			{
				if(KEYDOWN(prevInput,DIK_SPACE))
				{
					Sleep(100);
					ZeroMemory( &prevInput, sizeof(prevInput) );
				}else{
					if(g.getMenuY() == 0)
					{
						g.changeGameStatusHost();
					}else if(g.getMenuY() == 1)
					{
						g.changeGameStatusJOIN();
					}else
					{
						g.changeGameStatusSIngle();
						g.setupPlayers();
					}
				}
			}
			for(int i = 0; i < 256; ++i)
			{
				prevInput[i] = buffer[i];
			}
			ZeroMemory( &buffer, sizeof(buffer) );

		}
		else if(g.getGameStatus() == SETUPHOST){

		drawSetupHost();

		hr = g_lpDIDevice->GetDeviceState(sizeof(buffer),(LPVOID)&buffer); 

			if (KEYDOWN(buffer, DIK_ESCAPE))
			{
				if(KEYDOWN(prevInput,DIK_ESCAPE))
				{
					Sleep(100);
					ZeroMemory( &prevInput, sizeof(prevInput) );
				}else{
					// shutdown the directX Manager
					dxMgr->shutdown();
					return (int) msg.wParam;
					exit(0);
				}
			}
			for(int i = 0; i < 256; ++i)
			{
				prevInput[i] = buffer[i];
			}
			ZeroMemory( &buffer, sizeof(buffer) );
			g.setupHost();

		}else if(g.getGameStatus() == SETUPJOIN){

		drawSetupJoin();

		int numberOfInput();
		DialogBox(GetModuleHandle(NULL), MAKEINTRESOURCE(IDD_DIALOG1), NULL, DialogMessages);
		g.setupJoin(IPinput);

		}else
		{
			g.update();

			drawBoard(g);

			if(!g.gameOver)
			{
				hr = g_lpDIDevice->GetDeviceState(sizeof(buffer),(LPVOID)&buffer); 

				if (KEYDOWN(buffer, DIK_ESCAPE))
				{
					if(KEYDOWN(prevInput,DIK_ESCAPE))
					{
						Sleep(100);
						ZeroMemory( &prevInput, sizeof(prevInput) );
					}else{
						// shutdown the directX Manager
						dxMgr->shutdown();
						return (int) msg.wParam;
						exit(0);
					}
				}else if (KEYDOWN(buffer, DIK_P))
				{
					if(KEYDOWN(prevInput, DIK_P))
					{
						Sleep(100);
						ZeroMemory( &prevInput, sizeof(prevInput));
					}
					else
					{
						input = 'p';
						//g.handleInput(input);
					}
				}else if (KEYDOWN(buffer, DIK_UP))
				{
					if(KEYDOWN(prevInput,DIK_UP))
					{
						Sleep(100);
						ZeroMemory( &prevInput, sizeof(prevInput) );
					}else{
						input = 'w';
						//g.handleInput(input);
					}
				}else if (KEYDOWN(buffer, DIK_LEFT))
				{
					if(KEYDOWN(prevInput,DIK_LEFT))
					{
						Sleep(100);
						ZeroMemory( &prevInput, sizeof(prevInput) );
					}else{
						input = 'a';
						//g.handleInput(input);
					}
				}else if (KEYDOWN(buffer, DIK_DOWN))
				{
					if(KEYDOWN(prevInput,DIK_DOWN))
					{
						Sleep(100);
						ZeroMemory( &prevInput, sizeof(prevInput) );
					}else{
						input = 's';
						//g.handleInput(input);
					}
				}else if (KEYDOWN(buffer, DIK_RIGHT))
				{
					if(KEYDOWN(prevInput,DIK_RIGHT))
					{
						Sleep(100);
						ZeroMemory( &prevInput, sizeof(prevInput) );
					}else{
						input = 'd';
						//g.handleInput(input);
					}
				}else if (KEYDOWN(buffer, DIK_SPACE))
				{
					if(KEYDOWN(prevInput,DIK_SPACE))
					{
						Sleep(100);
						ZeroMemory( &prevInput, sizeof(prevInput) );
					}else{
						input = '\r';
						//g.handleInput(input);
					}
				}
				for(int i = 0; i < 256; ++i)
				{
					prevInput[i] = buffer[i];
				}
				ZeroMemory( &buffer, sizeof(buffer) );
				g.handleInput(input);
				input = '0';
			}
		}
	onLostDevice();

    }while( msg.message!=WM_QUIT && !g.gameOver && input != 27 );
	

	if(g.getP1()->getPieces() > g.getP2()->getPieces())
	{
		MessageBox(0, "Black Wins!!!", "Black Won", MB_OK);
	}
	else if(g.getP1()->getPieces() < g.getP2()->getPieces())
	{
		MessageBox(0, "White Wins!!!", "White Won", MB_OK);
	}
	else
	{
		MessageBox(0, "Tie Game!!!", "Tied", MB_OK);
	}

	// shutdown the directX Manager
	dxMgr->shutdown();

	return (int) msg.wParam;
}

bool initWindow(HINSTANCE hInstance)
{
	WNDCLASSEX wcex;
	DWORD      m_dwWindowStyle;			// this holds the window style

	wcex.cbSize = sizeof(WNDCLASSEX);
	wcex.style			= CS_HREDRAW | CS_VREDRAW;
	wcex.lpfnWndProc	= (WNDPROC)WndProc;
	wcex.cbClsExtra		= 0;
	wcex.cbWndExtra		= 0;
	wcex.hInstance		= hInstance;
	wcex.hIcon			= 0;
	wcex.hCursor		= LoadCursor(NULL, IDC_ARROW);
	wcex.hbrBackground	= (HBRUSH)(COLOR_WINDOW+1);
	wcex.lpszMenuName	= NULL;
	wcex.lpszClassName	= "Reversi";
	wcex.hIconSm		= 0;
	RegisterClassEx(&wcex);

	// if the application is to be fullscreen then set the window styles to
	// WS_EX_TOPMOST | WS_POPUP | WS_VISIBLE
	if (bFullScreen)
		m_dwWindowStyle = WS_EX_TOPMOST | WS_POPUP | WS_VISIBLE;
	// otherwise, we are doing a standard windowed application using
	// WS_OVERLAPPEDWINDOW
	else
		m_dwWindowStyle = WS_OVERLAPPEDWINDOW;

	//create window
	wndHandle = CreateWindow("Reversi", 
							 "Reversi", 
							 m_dwWindowStyle,	// pass the window style as a variable this time
							 CW_USEDEFAULT, 
							 CW_USEDEFAULT, 
							 windowSizeWidth, 
							 windowSizeHight, 
							 NULL, 
							 NULL, 
							 hInstance, 
							 NULL);
   if (!wndHandle)
      return false;
   
   ShowWindow(wndHandle, SW_SHOW);
   UpdateWindow(wndHandle);

   return true;
}

LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
	switch (message) 
	{
		case WM_DESTROY:
			PostQuitMessage(0);
			break;
	}
	return DefWindowProc(hWnd, message, wParam, lParam);
}


bool initSprites(void)
{
	GreenSurface = dxMgr->getSurfaceFromBitmap("Sprite/Green.png");
	WhiteSurface = dxMgr->getSurfaceFromBitmap("Sprite/white.png");
	BlackSurface = dxMgr->getSurfaceFromBitmap("Sprite/Black.png");

	// Setup Cursor
	CursorSurface = dxMgr->getSurfaceFromBitmap("Sprite/cursor.png");

	Source.left = 0;
	Source.top = 0;
	Source.bottom = SPRITE_WIDTH;
	Source.right = SPRITE_HEIGHT;

	// Setup Boarder
	BoarderSurface	= dxMgr->getSurfaceFromBitmap("Sprite/boarderTest.png");

	destRectBoarder.left = (OFFSETWIDTH - SPRITE_WIDTH);
	destRectBoarder.top = (OFFSETHIGHT - SPRITE_HEIGHT);
	destRectBoarder.bottom = destRectBoarder.top + HIGHTOFBOARDER;
	destRectBoarder.right = destRectBoarder.left + WIDTHOFBOARDER;

	SourceBoarder.left = 0;
	SourceBoarder.top = 0;
	SourceBoarder.bottom = HIGHTOFBOARDER;
	SourceBoarder.right = WIDTHOFBOARDER;


	// Setup Title
	TitleSurface = dxMgr->getSurfaceFromBitmap("Sprite/ReversiTitle.png");

	destRectTitle.left = (TITLE_OFFSET_X);
	destRectTitle.top = (TITLE_OFFSET_Y);
	destRectTitle.bottom = destRectTitle.top + HIGHTOFTITLE;
	destRectTitle.right = destRectTitle.left + WIDTHOFTITLE;

	SourceTitle.left = 0;
	SourceTitle.top = 0;
	SourceTitle.bottom = HIGHTOFTITLE ;
	SourceTitle.right = WIDTHOFTITLE ;

	// Setup Menu
	SetupGameSurface = dxMgr->getSurfaceFromBitmap("Sprite/setupMenu.png");
	
	destRectMenu.left = MENU_OFFSET_X;
	destRectMenu.top = MENU_OFFSET_Y;
	destRectMenu.bottom = destRectMenu.top + HIGHTOFMENU;
	destRectMenu.right = destRectMenu.left + WIDTHOFMENU;

	SourceMenu.left = 0;
	SourceMenu.top = 0;
	SourceMenu.bottom = HIGHTOFMENU;
	SourceMenu.right = WIDTHOFMENU;

	// Setup Checkmark
	CheckMarkSurface = dxMgr->getSurfaceFromBitmap("Sprite/checkmark.png");

	destRectCheck.left = CHECK_OFFSET_X;
	destRectCheck.top = CHECK_OFFSET_Y;
	destRectCheck.bottom = destRectCheck.top + HIGHTOFCHECK;
	destRectCheck.right = destRectCheck.left + WIDTHOFCHECK;

	SourceCheck.left = 0;
	SourceCheck.top = 0;
	SourceCheck.bottom = HIGHTOFCHECK;
	SourceCheck.right = WIDTHOFCHECK;

	// Setup Waiting for other players
	WaitingForOtherPlayersSurface = dxMgr->getSurfaceFromBitmap("Sprite/WaitingForOtherPlayers.png");

	destRectWait.left = WAIT_OFFSET_X;
	destRectWait.top = WAIT_OFFSET_Y;
	destRectWait.bottom = destRectCheck.top + HIGHTOFWAIT;
	destRectWait.right = destRectCheck.left + WIDTHOFWAIT;

	SourceWait.left = 0;
	SourceWait.top = 0;
	SourceWait.bottom = HIGHTOFWAIT;
	SourceWait.right = WIDTHOFWAIT;


	if (GreenSurface == NULL)
		return false;

	for (int c = 0; c < CALofSPRITES; c++)
	{
		for (int r = 0; r < ROWofSPRITES; r++)
		{
			spriteStruct[c][r].srcRect.top = 0;
			spriteStruct[c][r].srcRect.left = 0;
			spriteStruct[c][r].srcRect.right = SPRITE_WIDTH;
			spriteStruct[c][r].srcRect.bottom = SPRITE_HEIGHT;
			spriteStruct[c][r].posX = OFFSETWIDTH + (SPRITE_WIDTH * r);
			spriteStruct[c][r].posY = OFFSETHIGHT + (SPRITE_HEIGHT * c);
		}
	}

	return true;
}


void setupNetwork(Game & g)
{
	dxMgr->beginRender();

	// Render Menu
	dxMgr->blitToSurface(SetupGameSurface, &SourceMenu, &destRectMenu);


	// Render Title
	dxMgr->blitToSurface(TitleSurface, &SourceTitle, &destRectTitle);

	// Render Checkmark
	// Render Cursor

	if(g.getMenuY() == 0)
	{
	destRectCheck.left = CHECK_OFFSET_X;
	destRectCheck.top = CHECK_OFFSET_Y;
	destRectCheck.bottom = destRectCheck.top + HIGHTOFCHECK;
	destRectCheck.right = destRectCheck.left + WIDTHOFCHECK;
	}else if(g.getMenuY() == 1){
	destRectCheck.left = CHECK_OFFSET_X;
	destRectCheck.top = CHECK_OFFSET_Y + 64;
	destRectCheck.bottom = destRectCheck.top + HIGHTOFCHECK;
	destRectCheck.right = destRectCheck.left + WIDTHOFCHECK;
	}else if(g.getMenuY() == 2){
	destRectCheck.left = CHECK_OFFSET_X;
	destRectCheck.top = CHECK_OFFSET_Y + 128;
	destRectCheck.bottom = destRectCheck.top + HIGHTOFCHECK;
	destRectCheck.right = destRectCheck.left + WIDTHOFCHECK;
	}
	dxMgr->blitToSurface(CheckMarkSurface, &SourceCheck, &destRectCheck);

	dxMgr->endRender();	
}
void drawSetupHost()
{
	dxMgr->beginRender();

	// Render Title
	dxMgr->blitToSurface(TitleSurface, &SourceTitle, &destRectTitle);

	// Render Wait
	dxMgr->blitToSurface(WaitingForOtherPlayersSurface, &SourceWait, &destRectWait);

	dxMgr->endRender();	

}
void drawSetupJoin()
{
	dxMgr->beginRender();

	// Render Title
	dxMgr->blitToSurface(TitleSurface, &SourceTitle, &destRectTitle);

	dxMgr->endRender();	

}
void drawBoard(Game & g)
{
	dxMgr->beginRender();

	// Render Title
	dxMgr->blitToSurface(TitleSurface, &SourceTitle, &destRectTitle);

	// Render Boarder
	dxMgr->blitToSurface(BoarderSurface, &SourceBoarder, &destRectBoarder);

	for (int c = 0; c < CALofSPRITES; c++)
	{
		for (int r = 0; r < ROWofSPRITES; r++)
		{			
			RECT destRect;
			destRect.left = spriteStruct[c][r].posX;
			destRect.top = spriteStruct[c][r].posY;
			destRect.bottom = destRect.top + SPRITE_HEIGHT;
			destRect.right = destRect.left + SPRITE_WIDTH;
			if( g.getBoard(c,r) == 'w')
			{
				dxMgr->blitToSurface(WhiteSurface, &spriteStruct[c][r].srcRect, &destRect);
			}else if( g.getBoard(c,r) == 'b')
			{
				dxMgr->blitToSurface(BlackSurface, &spriteStruct[c][r].srcRect, &destRect);
			}else{
				dxMgr->blitToSurface(GreenSurface, &spriteStruct[c][r].srcRect, &destRect);
			}
			
		}
	}

	RECT Source;

	if (g.getBoard(g.getY(), g.getX()) == 'w')
	{
		Source.left = 0;
	}

	if (g.getBoard(g.getY(), g.getX()) == 'g')
	{
		Source.left = 32;
	}

	if (g.getBoard(g.getY(), g.getX()) == 'b')
	{
		Source.left = 64;
	}

	Source.top = 0;
	Source.bottom = Source.top + SPRITE_HEIGHT;
	Source.right = Source.left + SPRITE_WIDTH;

	// Render Cursor
	destRect.left = OFFSETWIDTH + (SPRITE_WIDTH * g.getX());
	destRect.top = OFFSETHIGHT + (SPRITE_WIDTH * g.getY());
	destRect.bottom = destRect.top + SPRITE_HEIGHT;
	destRect.right = destRect.left + SPRITE_WIDTH;

	dxMgr->blitToSurface(CursorSurface, &Source, &destRect);

	
	RECT rct;
	int whatever = 0;
	char string1[] = "Total Black Pieces: ";
	char string4[] = "Total White Pieces: ";
	char string2[25];
	char string3[25];


	if (g.getCurrPlayer()->getSelf() == 'b')
	{
		rct.left=50;
		rct.right=280;
		rct.top=200;
		rct.bottom=rct.top+25;

		dxMgr->DisplaySomeText("Black's turn", rct);

		rct.left=25;
		rct.right=380;
		rct.top=250;
		rct.bottom=rct.top+25;

		whatever = g.getCurrPlayer()->getPieces();

		itoa(whatever, string2, 10);

		strcpy(string3, string1);
		strcat(string3, string2);

		dxMgr->DisplaySomeText(string3, rct);

		
		rct.left=600;
		rct.right=800;
		rct.top=250;
		rct.bottom=rct.top+25;

		whatever = g.getNextPlayer()->getPieces();

		itoa(whatever, string2, 10);

		strcpy(string3, string4);
		strcat(string3, string2);

		dxMgr->DisplaySomeText(string3, rct);
	}
	else
	{
		rct.left=625;
		rct.right=780;
		rct.top=200;
		rct.bottom=rct.top+25;

		dxMgr->DisplaySomeText("White's turn", rct);

		rct.left=25;
		rct.right=380;
		rct.top=250;
		rct.bottom=rct.top+25;

		whatever = g.getNextPlayer()->getPieces();

		itoa(whatever, string2, 10);

		strcpy(string3, string1);
		strcat(string3, string2);

		dxMgr->DisplaySomeText(string3, rct);


		rct.left=600;
		rct.right=800;
		rct.top=250;
		rct.bottom=rct.top+25;

		whatever = g.getCurrPlayer()->getPieces();

		itoa(whatever, string2, 10);

		strcpy(string3, string4);
		strcat(string3, string2);

		dxMgr->DisplaySomeText(string3, rct);



	}

	dxMgr->endRender();
}

void onLostDevice()
{
	HRESULT hr;

	if(g_lpDIDevice != NULL)
		hr = g_lpDIDevice->Acquire();

}
void onResetDevice()
{
	g_lpDIDevice->Acquire();
}
